package org.NooLab.texx;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;

import org.NooLab.utilities.files.DFutils;
import org.NooLab.utilities.logging.PrintLog;


 
import org.NooLab.itexx.storage.DbFieldRequests;
import org.NooLab.itexx.storage.docserver.storage.DocoDataBaseIntf;
import org.NooLab.itexx.storage.docserver.storage.ObservedDocument;
import org.NooLab.itexx.storage.docserver.storage.ObservedDocuments;


import org.NooLab.texx.MethodDispatcher.WorkerProcess;
import org.NooLab.texx.components.WorkDataObject;
import org.NooLab.texx.properties.TexxProperties;
import org.NooLab.texx.storage.DocObsDataBase;
import org.NooLab.texx.storage.DocoDataBase;



/**
 * 
 * 
 * 
 *
 */
public class MethodDispatcherServer {

	MethodDispatcher medis;
	TexxTask txxTask;
	private MethodDispatcherDigester workResultHandler;
	private TexxFactory texxFactory;
	private TexxProperties texxProperties;
	
	DocObsDataBase dbFetcher; 
	DocoDataBaseIntf docoDataBase ;
	
	boolean processAccessAllowed=true;
	int parallelDocDigest = 1;
	int numberOfDocsPerChunk = 1 ; // typically 10..50 
	
	ArrayList<String> deliveredWorkPackages = new ArrayList<String>();
	
	ServiceServer service ;
	boolean isRunning;
	
	PrintLog out;
	
	
	// ========================================================================
	public MethodDispatcherServer(MethodDispatcher medis, TexxTask task) throws Exception {
		this.medis = medis ;
		txxTask = task;
		texxFactory = medis.texxFactory;
		
		texxProperties = medis.texxProperties;
		workResultHandler = new MethodDispatcherDigester( medis , texxFactory);
	
		txxTask.setWorkResultHandler( workResultHandler  ) ;
		
		dbFetcher = new DocObsDataBase(texxProperties);
		//c  medis.texxDataBase.getStorageDir( )

		docoDataBase = dbFetcher.getDocoDataBase() ; 
		docoDataBase.setStorageFolder( medis.texxDataBase.getStorageDir( ) );

		if (dbFetcher.databaseIsRunningAsServer()==false){
			throw(new Exception("Database for observation of documents is not available. \nPlease start the DocumentObserver before using Texx in 'service' mode. \n\n"));
		}

		
		
		
		service = new ServiceServer(); // starts the thread and its loop
		out = medis.getOut();
	}
	// ========================================================================
	
	public void go() {
		
		service.start() ;
	}

	public void close(){
	
		isRunning=false;
	}
	
	class DocumentRouter {
		String filename;
		long docId ;
		ObservedDocument obsDoc = null;
		 
		public DocumentRouter( ObservedDocument od){ // String filename){
			 filename = od.getDocname();
			 docId = od.getDocId() ;

			 obsDoc = od;
		}
		
		public WorkDataObject performOnObservedDocument( ) throws Exception {
			// 
			WorkDataObject workDataObj =null;
			
			if (DFutils.fileExists(filename)==false){
				return workDataObj;
			}
			out.setPrefix("[TEXX]");
			out.print(2, "... texx-server going to work on document : "+filename);
			out.delay(20);
			
			workDataObj = new WorkDataObject(texxProperties) ;
			
			if ( txxTask.getSourceType() <= TexxTaskIntf._TEXXTASK_SOURCE_TYPE_FILE){
				texxProperties.setDataSrcFilename(filename);
				// docId = ...get the last one from docosrv, and update if necessary and possible
				docId = filename.hashCode() ;
			}
			
			workDataObj.setSourceId(docId) ;
			workDataObj.setSourceUrl(filename) ;
			 
			
			deliveredWorkPackages.add( workDataObj.getReceipt() ) ;
			
			// medis.checkConsistencyOfRequest() ;
			out.print(3, "... calling <medis.perform> for doc : "+filename);
			
			// medis.perform( workDataObj ); 
			// returns a new one by: return  new WorkerProcess
			WorkerProcess workerProcess = medis.getWorkerProcess() ;
			workerProcess.setDataObj( new WorkDataObject(workDataObj,true) ) ;
			workerProcess.start() ;
			
			return workDataObj;
		}

	}

	
	class ServiceServer implements Runnable{

		Thread medisBatchSvcThread;
		
		// ArrayList<String> observedDocumentsDue = new ArrayList<String>();
		ObservedDocuments observedDocumentsDue = new ObservedDocuments (); 
		
		public ServiceServer(){
			isRunning=false;
			medisBatchSvcThread = new Thread(this, "medisBatchSvcThread") ;
			 
		}
		
		public void stop(){
			isRunning=false;
		}
		
		public void start(){
			medisBatchSvcThread.start() ;
		}
		
		@Override
		public void run() {
			boolean recentlyFoundDoc = false;
			WorkDataObject wdo;
			
			isRunning = true;
			while (isRunning ){ // userbreak
				
				if (recentlyFoundDoc){
					
					try {
						
						if (parallelDocDigest>1){
							processAccessAllowed = true;
						}
						
						wdo = (new DocumentRouter( getNext() )).performOnObservedDocument();
						
						out.delay(5);
						if (parallelDocDigest<=1){
							// wait for 
							out.print(4, "  .......  waiting for confirmed receipt : "+ wdo.getReceipt() );
							if (wdo!=null){
								while (  
										(medis.finishedWorkDataObjects.indexOf(wdo.getSourceId())<0) ){
									out.delay(10);
								}
								out.printErr(2, "\nwork on data object "+wdo.getReceipt()+"  \n"+
										        "            on file <url: "+wdo.getSourceUrl()+"> \n" +
										        "            has been finished." );
								wdo.setFinished(true);
							}
						}
						
						
					} catch (Exception e) {
						e.printStackTrace();
					}
					
					recentlyFoundDoc = observedDocumentsDue.size()>0;
					
					if (recentlyFoundDoc==false){
						// break; // TODO REMOVE  abc124 DEBUG ONLY !!!!!!!!!!!!!  <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
					}
				}else{
					// recentlyFoundDoc == false;
					
					if (isRunning){
						
						consolidateProcessTraces();
						
						// only retrieve the next couple of docs if the last packages has been worked through
						if (deliveredWorkPackages.size()<=1 ){
							recentlyFoundDoc = checkForNewDocuments( numberOfDocsPerChunk ) ; // max number
						}
					}
					
					if (recentlyFoundDoc==false){
						out.delay(5000) ; // make it a dynamic value, also with user-based constraints
						out.print(4,"no new documents found... waiting...");
					}
				}
				
				
			}// ->
			
		}

		private void consolidateProcessTraces() {

			if (medis.completedWorkerProcess.size()==0){
				return;
			}

			for (int i=medis.completedWorkerProcess.size()-1;i>=0;i--){
				String workguid = medis.completedWorkerProcess.get(i);
				int p = deliveredWorkPackages.indexOf(workguid);
				if ( p>=0){
					deliveredWorkPackages.remove(p) ;
					medis.completedWorkerProcess.remove(i) ;
				}
			}

		}

		private ObservedDocument getNext() { 
			ObservedDocument od=null;
			String fname = "" ;
			
			if (observedDocumentsDue.size()>0){
				
				fname = observedDocumentsDue.get(0).getDocname() ;
				od = observedDocumentsDue.get(0) ;
				
				observedDocumentsDue.remove(0) ;
			}
			return od;
		}

		private boolean checkForNewDocuments(int maxCount) {
			
			boolean rB=false;
			
			// check the database
			
			// ArrayList<String> docs = new ArrayList<String>();
			// is now inside of this...
			ObservedDocuments docs = new ObservedDocuments (); 
			
			DbFieldRequests accvalues = null;
			
			if (docoDataBase.getConnection()!=null){
				try {
					dbFetcher.connection.close() ;
				} catch (Exception e) {}
			}
			dbFetcher.connection = docoDataBase.getWireLessDatabaseServer("TEXX","texx") ;
				// docoDataBase = dbFetcher.getDocoDataBase() ;
			
			
			docs = docoDataBase.getDocumentsDue( maxCount, 0, accvalues) ; 
			/* 0 = all "due"-types, 
			       but not moves ! (DocumentObserverIntf._FILE_CHANGE_MOVE), these are handled separately through 
			       chained updates from db level to SomStore (somewhere we get a translation of indices
			       (internal logical, external physical), which not necessarily are the same (only on creation or deep update)
			       
			
			*/
			
			if ((docs!=null) && (docs.size()> 0)){
				rB = true;
				for (int i=docs.size()-1;i>=0;i--){
					if (DFutils.fileExists(docs.get(i).getDocname())==false){
						docs.remove(i) ;
					}
				}
				if (docs.size()>0){
					observedDocumentsDue.addAll(docs) ;
					docs.clear() ;
				}
			}
			
			return rB;
		}
		
	}


	public MethodDispatcherDigester getWorkResultHandler() {
		return workResultHandler;
	}

	public boolean isRunning() {
		return isRunning;
	}

	public int getParallelDocDigest() {
		return parallelDocDigest;
	}

	public void setParallelDocDigest(int parallelDocDigest) {
		this.parallelDocDigest = parallelDocDigest;
	}

	public boolean isProcessAccessAllowed() {
		return processAccessAllowed;
	}

	public void setProcessAccessAllowed(boolean processAccessAllowed) {
		this.processAccessAllowed = processAccessAllowed;
	}
}
